﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MongoDB.Driver;
using Sustainalytics.BusinessResearchProductStates.Entities.ResearchState;
using Sustainalytics.CRT.Synchronization.Exception;
using Sustainalytics.Synchronization.Interface;
using Sustainalytics.MongoDB.Extensions;
using Sustainalytics.ESG.Entities;
using Sustainalytics.Entities.ESGRatings;
using Sustainalytics.ESGRatings.BusinessLogic;

namespace Sustainalytics.CRT.Synchronization
{
    public class FocusedRatingsComputation
    {
        private class CompanyData
        {
            public string CompanyName { get; set; }

            public Guid Id { get; set; }
            public string PeerGroup { get; set; }

            public List<int> UniverseIds { get; set; }
        }

        public async Task Compute(SyncContext syncContext, bool hasImportedScores = false)
        {
            var clientDwConnectionString = syncContext.GAConnectionStrings["ClientDW"];


            var resultCollection = MongoFactory.GetCollectionFromDatabase<RPCompanyESGComputationResult>(clientDwConnectionString, "RPCompanyESGComputationResult");
            var corporateDataCollection = MongoFactory.GetCollectionFromDatabase<ClientDW.Entities.CorporateData>(clientDwConnectionString);
            var financialDataCollection = MongoFactory.GetCollectionFromDatabase<ClientDW.Entities.FinancialData>(clientDwConnectionString);

            var aggregatesRatingsHelper = new FocusedRatingAggregatesProcessor(resultCollection, corporateDataCollection);

            var marketCapitalizationHelper = new MarketCapitalizationProcessor(corporateDataCollection, resultCollection, financialDataCollection);

            var peerGroupPercentileThresholds = MongoFactory.GetCollectionFromDatabase<Sustainalytics.Entities.ESGRatings.EsgFocusPercentileThreshold>(clientDwConnectionString).Find(_ => true)
                                        .ToListAsync().Result.GroupBy(x => x.PeerGroup).ToDictionary(x => x.Key, x => x.ToList());

            var peerGroupAssesmentsThresholds = MongoFactory.GetCollectionFromDatabase<Sustainalytics.Entities.ESGRatings.EsgFocusThresholds>(clientDwConnectionString).Find(_ => true)
                                        .ToListAsync().Result.GroupBy(x => x.PeerGroup).ToDictionary(x => x.Key, x => x.ToArray());


            var companyScoreManager = new FocusedRatingsCompanyScoreManager(clientDwConnectionString, new FocusedRatingsCompanyScoreCalculator());

            var computedCompaniesCounter = 0;

            await corporateDataCollection
                .Find(x => x.CoreEsgResearchState.ResearchState == ResearchState.ResearchAvailable &&
                           x.ResearchParentId == null)
                .Project(
                    x =>
                        new CompanyData
                        {
                            Id = x.Id,
                            CompanyName = x.CompanyName,
                            PeerGroup = x.PeerGroup,
                            UniverseIds = x.UniverseIds
                        })
                .ForEachAsync(async company =>
                {
                    await
                        Processor(company, syncContext, companyScoreManager,
                                peerGroupPercentileThresholds, peerGroupAssesmentsThresholds, aggregatesRatingsHelper,
                                marketCapitalizationHelper, resultCollection)
                            .ConfigureAwait(false);
                    Interlocked.Increment(ref computedCompaniesCounter);
                })

                .ConfigureAwait(false);


            syncContext.Log($"{computedCompaniesCounter} focused ratings companies computed.");
        }

        private static async Task Processor(CompanyData company, SyncContext syncContext, FocusedRatingsCompanyScoreManager companyScoreManager, Dictionary<string, List<EsgFocusPercentileThreshold>> peerGroupPercentileThresholds, Dictionary<string, EsgFocusThresholds[]> peerGroupAssesmentsThresholds, FocusedRatingAggregatesProcessor aggregatesRatingsHelper, MarketCapitalizationProcessor marketCapitalizationHelper, IMongoCollection<RPCompanyESGComputationResult> resultCollection)
        {
            try
            {
                List<EsgFocusPercentileThreshold> percentileThresholds;
                EsgFocusThresholds[] assesmentsThresholds;

                var computationResult = new RPCompanyESGComputationResult
                {
                    Id = company.Id,
                    IsInReferenceUniverse = true
                };

                await companyScoreManager.LoadCompanyScoreAsync(computationResult);

                if (peerGroupPercentileThresholds.TryGetValue(company.PeerGroup, out percentileThresholds) && peerGroupAssesmentsThresholds.TryGetValue(company.PeerGroup, out assesmentsThresholds))
                {

                    computationResult.OverallPerformance.Percentile = await FocusedRatingsPercentileManager.GetPercentile(computationResult.OverallPerformance.Score, percentileThresholds).ConfigureAwait(false);

                    computationResult.OverallPerformance.Assessment = FocusedRatingsAssessmentManager.GetAssessment(computationResult.OverallPerformance.Score, assesmentsThresholds);

                    aggregatesRatingsHelper.AppendTop5ToResult(computationResult, company.PeerGroup, company.CompanyName);

                    aggregatesRatingsHelper.AppendGroupAggregates(computationResult, company.PeerGroup);

                    marketCapitalizationHelper.AppendPeersListToResult(computationResult, company.PeerGroup);

                    await resultCollection.DeleteOneAsync(x => x.Id == company.Id).ConfigureAwait(false);

                    await resultCollection.InsertOneAsync(computationResult).ConfigureAwait(false);

                }
                else
                {
                    syncContext.Log($"{company.Id} {company.CompanyName} PeerGroup {company.PeerGroup} not found in  EsgFocusPercentileThreshold ");
                }
            }
            catch (CompanyRawScoresNotFoundException ex)
            {
                syncContext.Log($"{ex.Message} - {company.CompanyName}");
            }
            catch (CompanyWeightsNotFoundException ex)
            {
                syncContext.Log($"{ex.Message} - {company.CompanyName}");
            }
            catch (IndicatorNotFoundException ex)
            {
                syncContext.Log($"{ex.IndicatorCode} Indicator not found! for company: {company.Id} - {company.CompanyName}");
            }
            catch (CompanyEsgComputationResultNotFoundException ex)
            {
                syncContext.Log($"{ex.Message} - {company.CompanyName}");
            }
            catch (System.Exception ex)
            {
                syncContext.Log(ex.ToString());
                throw;
            }
        }
    }
}

